package com.hmdp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import static com.hmdp.utils.RedisConstants.LOCK_ORDER_KEY;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Resource
    private IVoucherOrderService voucherOrderService;
    @Resource
    private RedisIdWorker redisIdWorker;


    @Resource
    private RedissonClient redissonClient;
    /**
     * 创建秒杀订单
     * @param voucherId
     * @return
     */
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1.查看优惠卷信息
        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
        log.info("seckillVoucher:{}",seckillVoucher);
        //2.判断秒杀是否开始
        if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }
        if (seckillVoucher.getEndTime().isEqual(LocalDateTime.now())){
            return Result.fail("秒杀已经结束");
        }
        //3.判断库存是否充足
        if (seckillVoucher.getStock()<1){
            return Result.fail("库存不足");
        }
        /**
         * 会出现线程问题
         */
        UserHolder.saveUser(UserDTO.builder().id("6").build());
        Long userId = Long.parseLong(UserHolder.getUser().getId());
        /**
         * 为什么不用toString()方法，因为toSting()方法会生成新的对象，而intern()方法会直接返回常量池中的对象
         */
        /**synchronized(userId.toString().intern()){
        RedosLock redosLock = new RedosLock(stringRedisTemplate,"order"+userId);
        boolean b = redosLock.tyeLock(1200);**/
        //
        RLock lock = redissonClient.getLock(LOCK_ORDER_KEY + userId);
        boolean b = true;
        b = lock.tryLock();
        if (!b){
            return Result.fail("一人只能下一单");
        }
        try {
        /**
             *
             *  return KillOrder(voucherId, seckillVoucher);
             *  @Transactional 是不被VoucherOrderServiceImpl代理对象代理的,所有事务会失效
             *  return this.KillOrder(voucherId, seckillVoucher);是目标对象，没有事务功能
             *  问题: 事务失效的几种原因
             *  用到的是：动态代理
             */
            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            return proxy.KillOrder(voucherId, seckillVoucher);
        } finally {
            lock.unlock();
        }
        //        }

    }
    /**
     * 秒杀订单(解决线程问题)
     * @param voucherId
     * @param seckillVoucher
     * @return
     */
    @Transactional
    public Result KillOrder(Long voucherId, SeckillVoucher seckillVoucher) {

        /**
         * 会出现线程问题
         */
        // 4.一人只能抢一单
        int count = voucherOrderService.count(new LambdaQueryWrapper<VoucherOrder>()
                .eq(VoucherOrder::getUserId, "6"));
        if (count>0){
            return Result.fail("一人只能抢一单");
        }
        /**
         * 乐观锁
         */
        //5.扣减库存
        boolean succeed = seckillVoucherService.update()
                .setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0).update();

        if (!succeed) {
            return Result.fail("库存不足");
        }
        //6.创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
//        String id = UserHolder.getUser().getId();
        voucherOrder.setId(redisIdWorker.nextId("6"));
        voucherOrder.setUserId(6L);
        voucherOrder.setVoucherId(seckillVoucher.getVoucherId());
        voucherOrderService.save(voucherOrder);
        return Result.ok();
    }
}
